package com.car.loan.common.utils.qiniu;


import com.alibaba.fastjson.JSONException;
import com.car.loan.common.utils.IdUtils;
import com.car.loan.framework.redis.RedisCache;
import com.google.gson.Gson;
import com.qiniu.api.io.IoApi;
import com.qiniu.api.io.PutExtra;
import com.qiniu.api.io.PutRet;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Recorder;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.persistent.FileRecorder;
import com.qiniu.util.Auth;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.security.auth.message.AuthException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author huangding
 * @description 七牛上传处理
 * @date 2019/1/18 13:30
 */
@Slf4j
@Component
public class QiniuUtil {


    @Resource
    private QiniuConfig qiniuConfig;


    @Autowired
    private RedisCache redisCache;

    @Autowired
    private RedisTemplate redisTemplate;

    private Auth auth;

    @PostConstruct
    public void init() {
        auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey());
    }


    public String uploadBase640(MultipartFile file, String prefix) throws IOException {
        String fileName =
            prefix + "/" + IdUtils.simpleUUID() + "." + Objects
                .requireNonNull(file.getOriginalFilename()).split("\\.")[1];
        return putBase640(
            qiniuConfig.getBucketName(), file.getBytes(), fileName);
    }

    public String uploadBase640(byte[] buffer, String fileName) throws QiniuException {
        return putBase640(
            qiniuConfig.getBucketName(), buffer, fileName);
    }

    /**
     * https 版本
     */
    public String getAuthUrl(String url) {
        url = url + qiniuConfig.getStyleSplitterShow();
        return auth
            .privateDownloadUrl(qiniuConfig.getCdn() + url);
    }

    public String getAuthUrlOriginal(String url) {
        return auth
            .privateDownloadUrl(qiniuConfig.getCdn() + url);
    }

    public String getAuthUrlOriginalHttp(String url) {
        return auth
            .privateDownloadUrl(qiniuConfig.getHttpCdn() + url);
    }

    /**
     * http的缩略图
     */
    public String getUrlByThumbnailHttps(String url) {
        url = url + qiniuConfig.getStyleSplitter();
        return auth
            .privateDownloadUrl(qiniuConfig.getCdn() + url);
    }

    /**
     * http 版本
     */
    public String getAdminAuthUrl(String url) {
        url = url + qiniuConfig.getStyleSplitterShow();
        return auth
            .privateDownloadUrl(qiniuConfig.getHttpCdn() + url);
    }

    /**
     * http的缩略图
     */
    public String getUrlByThumbnail(String url) {
        url = url + qiniuConfig.getStyleSplitter();
        return auth
            .privateDownloadUrl(qiniuConfig.getHttpCdn() + url);
    }


    /**
     * 华东
     */
    public String putInputStream0(String accessKey, String secretKey, String bucketName,
        ByteArrayInputStream byteArrayInputStream,
        String fileName) {
        return putInputStream(accessKey, secretKey, Zone.zone0(), bucketName, byteArrayInputStream,
            fileName);
    }

    public String putUrl0(String urlStr,
        String fileName) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3 * 1000);
        //得到输入流
        InputStream inputStream = conn.getInputStream();
        String s = uploadBase640(readInputStream(inputStream), fileName);
        inputStream.close();
        return s;
    }


    /**
     * 从输入流中获取字节数组
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while ((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }


    public String putBase640(String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        return putBase64(Zone.zone0(), bucketName, bytes, fileName);
    }

    public void deleteZone0ByKey(String key) {
        delete(Zone.zone0(), key);
    }

    /**
     * 华北
     */
    public String putBase641(String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        return putBase64(Zone.zone1(), bucketName, bytes, fileName);
    }

    /**
     * 华南
     */
    public String putBase642(String accessKey, String secretKey, String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        return putBase64(Zone.zone2(), bucketName, bytes, fileName);
    }

    /**
     * 北美
     */
    public String putBase64Na0(String accessKey, String secretKey, String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        return putBase64(Zone.zoneNa0(), bucketName, bytes, fileName);
    }

    /**
     * 东南亚
     */
    public String putBase64As0(String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        return putBase64(Zone.zoneAs0(), bucketName, bytes, fileName);
    }

    /**
     * 生成上传凭证
     */
    private String getUpToken(String bucket) {
        return getRedisUpToken(bucket);
    }

    /**
     * 获取token
     */
    public String getRedisUpToken() {
        return auth.uploadToken(qiniuConfig.getBucketName());
    }

    private String getRedisUpToken(String bucket) {
        String key = "qiniu_up_token:" + bucket;
        Object cacheObject = redisCache.getCacheObject(key);
        if (null == cacheObject) {
            String s = auth.uploadToken(bucket);
            ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(key, s, 3600L - 400L, TimeUnit.SECONDS);
            return s;
        }
        return cacheObject.toString();
    }

    private String putInputStream(String accessKey, String secretKey, Zone zone,
        String bucketName,
        ByteArrayInputStream byteInputStream,
        String fileName) {
        Configuration cfg = new Configuration(zone);
        UploadManager uploadManager = new UploadManager(cfg);
        try {
            Response response = uploadManager
                .put(byteInputStream, fileName, getUpToken(bucketName), null,
                    null);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson()
                .fromJson(response.bodyString(), DefaultPutRet.class);
            return putRet.key;
        } catch (QiniuException ex) {
            Response r = ex.response;
            log.error("七牛上传出错：{}", r.toString(), ex);
            try {
                ex.printStackTrace();
                log.error("七牛上传出错：{}", r.bodyString());
            } catch (QiniuException ex2) {
                log.error("七牛上传出错：{}", ex2);
            }
        }
        return null;
    }


    /**
     * 上传base64图片
     *
     * @param zone 华东	Zone.zone0() 华北	Zone.zone1() 华南	Zone.zone2() 北美	Zone.zoneNa0()东南亚	Zone.zoneAs0()
     * @param bucketName 空间名
     * @param fileName 名称
     */
//    private String putBase64(Zone zone,
//        String bucketName,
//        String base64,
//        String fileName) throws QiniuException {
//        //构造一个带指定Zone对象的配置类
//        Configuration cfg = new Configuration(zone);
//        //...其他参数参考类注释
//        UploadManager uploadManager = new UploadManager(cfg);
//        byte[] bytes = base64String2ByteFun(base64);
//        try {
//            Response response = uploadManager
//                .put(bytes, fileName, getUpToken(bucketName));
//            //解析上传成功的结果
//            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
//            return putRet.key;
//        } catch (QiniuException ex) {
//            log.error("七牛上传出错：{}", ex);
//            throw new QiniuException(ex);
//        }
//    }
    private String putBase64(Zone zone,
        String bucketName,
        byte[] bytes,
        String fileName) throws QiniuException {
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(zone);
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        try {
            Response response = uploadManager
                .put(bytes, fileName, getUpToken(bucketName));
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            return putRet.key;
        } catch (QiniuException ex) {
            log.error("七牛上传出错：{}", ex.getMessage(), ex);
            throw new QiniuException(ex);
        }
    }

    public boolean uploadFile(String fileName, InputStream in)
        throws AuthException, JSONException {
        long startTime = System.currentTimeMillis();
        // 可选的上传选项，具体说明请参见使用手册。
        PutExtra extra = new PutExtra();
        // 上传文件
        PutRet ret = IoApi.Put(getRedisUpToken(), fileName, in, extra);
        if (ret.ok()) {
            log.info("视频文件上传成功!");
            long endTime = System.currentTimeMillis();
            log.info("视频文件上传用时：" + (endTime - startTime) + "ms");
            return true;
        } else {
            log.error("视频文件上传失败!");
            return false;
        }
    }

    public boolean breakpointUpload(Zone zone, String key, InputStream in) {
//构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(zone);
//如果是Windows情况下，格式是 D:\\qiniu\\test.png
        String localFilePath = "/home/qiniu/test.mp4";
        String localTempDir = Paths
            .get(System.getenv
                ("java.io.tmpdir"), qiniuConfig.getBucketName()).toString();

        try {
            //设置断点续传文件进度保存目录
            FileRecorder fileRecorder = new FileRecorder(localTempDir);
            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
            try {
                Response response = uploadManager.put(in, key, getRedisUpToken(), null, null);
                //解析上传成功的结果
                DefaultPutRet putRet = new Gson()
                    .fromJson(response.bodyString(), DefaultPutRet.class);
            } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
                } catch (QiniuException ex2) {
                    //ignore
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return true;
    }


    public void delete(Zone zone, String key) {

        Configuration c = new Configuration(zone);
        // 实例化一个BucketManager对象
        BucketManager bucketManager = new BucketManager(auth, c);
        // 要测试的空间和key，并且这个key在你空间中存在
        String bucket = qiniuConfig.getBucketName();
        try {
            // 调用delete方法移动文件
            bucketManager.delete(bucket, key);
            log.info("文件删除成功!.[fileName={}]", key);
        } catch (QiniuException e) {
            log.error("文件删除失败!.[fileName={}]", key);
            // 捕获异常信息
            Response r = e.response;
            log.error(r.toString());
        }
    }

    /**
     * base64字符串转byte[]
     */
    private static byte[] base64String2ByteFun(String base64Str) {
        return Base64.decodeBase64(base64Str);
    }

    /**
     * byte[]转base64
     */

    public static String byte2Base64StringFun(byte[] b) {
        return Base64.encodeBase64String(b);
    }


}
